package table;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;

import utils.Utils;

public class TableExport {
	private BufferedWriter out;
	private EObject root;
	private Map<EObject, String> obj_path;
	
	public TableExport(String in_file, String ext, EPackage pack, String out_file) {
		root = Utils.loadInstance(in_file, ext, pack).getContents().get(0);
		obj_path = new HashMap<EObject, String>();
		
		try {
			out = new BufferedWriter(new FileWriter(out_file));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void exportElements() throws IOException {
		exportElements(root, "");
	}
	
	private void save() {
		try {
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void run() {
		try {
			exportElements();
			exportReferences();
		} catch (IOException e) {
			e.printStackTrace();
		}
		save();
	}
	
	/**
	 * sauvegarde les elements et les attributs
	 * @throws IOException 
	 */
	@SuppressWarnings("unchecked")
	private void exportElements(EObject obj, String path) throws IOException {
		EClass c = obj.eClass();
		List<EObject> next = new ArrayList<EObject>();
		
		for (EReference ref: c.getEAllReferences()) {
			int nb_att = ((EClass)ref.getEType()).getEAllAttributes().size();
			if (ref.isContainment()) {
				
				if (ref.isMany()) {
					List<EObject> os = (List<EObject>)obj.eGet(ref);
					int i = 0;
					for (EObject o: os) {
						String p;
						if (path.equals(""))
							p = ref.getName() + "[" + i + "]";
						else
							p = path + "." + ref.getName() + "[" + i + "]";
						
						obj_path.put(o, p);
						next.add(o);

						if ((nb_att > 0) && (os.size() > 0)) {							
							out.write("\n% TYPES *");
							for (int j = 0; j < nb_att; j++) {
								out.write(" *");
							}
							out.write("\n");
							
							for (int j = 0; j < nb_att; j++) {
								out.write("% ADD [0] -> " + ((EClass)ref.getEType()).getEAllAttributes().get(j).getName() + " -> [" + (j+1) + "]\n");
							}

							out.write(p);
							for (int j = 0; j < nb_att; j++) {
								EAttribute att = ((EClass)ref.getEType()).getEAllAttributes().get(j);
								out.write(" " + o.eGet(att));
							}
							out.write("\n");
						}						
						i++;
					}
				}
				else {
					EObject o = (EObject)obj.eGet(ref);
					String p;
					if (path.equals(""))
						p = ref.getName() + "[0]";
					else
						p = path + "." + ref.getName() + "[0]";

					obj_path.put(o, p);
					next.add(o);

					if ((nb_att > 0) && (o != null)) {							
						out.write("\n% TYPES *");
						for (int j = 0; j < nb_att; j++) {
							out.write(" *");
						}
						out.write("\n");
						
						for (int j = 0; j < nb_att; j++) {
							out.write("% ADD [0] -> " + ((EClass)ref.getEType()).getEAllAttributes().get(j).getName() + " -> [" + (j+1) + "]\n");
						}
						out.write(p);
						for (int j = 0; j < nb_att; j++) {
							EAttribute att = ((EClass)ref.getEType()).getEAllAttributes().get(j);
							out.write(" " + o.eGet(att));
						}
						out.write("\n");
					}
				}
			}
		}
		
		// recursivite
		for (EObject o: next) {
			exportElements(o, obj_path.get(o));
		}
	}	

	@SuppressWarnings("unchecked")
	private void exportReferences() throws IOException {
		for (EObject o: obj_path.keySet()) {
			EClass c = o.eClass();
			
			for (EReference ref: c.getEAllReferences()) {
				if (!ref.isContainment()) {
					
					if (ref.isMany()) {
						List<EObject> os = (List<EObject>)o.eGet(ref);
						
						if (os.size() > 0) {
							out.write("\n% TYPES * *\n");
							
							out.write("% ADD [0] -> " + ref.getName() + " -> [1 +]\n");

							out.write(obj_path.get(o));
							for (int j = 0; j < os.size(); j++) {
								out.write(" " + obj_path.get(os.get(j)));
							}
							out.write("\n");
						}
					}
					else {
						EObject obj = (EObject)o.eGet(ref);
						if (obj != null) {
							out.write("\n% TYPES * *\n");
							
							out.write("% ADD [0] -> " + ref.getName() + " -> [1]\n");
	
							out.write(obj_path.get(o));
							out.write(" " + obj_path.get(obj));
							out.write("\n");
						}
					}
				}
			}
		}
	}	
}
